//! ifun_grep is a string search library
//!
//! Supports case sensitive search.
//!

use ansi_term::Colour::{Red, Yellow};
use anyhow::{Context, Result};
use clap::Parser;
use log;
use std::fs;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum IfunError {
    #[error("the file is't exist")]
    FileNotExist(#[from] std::io::Error),
}

/// the struct `Config` defines command line params.
///
/// # Example
///
/// ```
/// let search = String::from("let");
/// let config = ifun_grep::Config {
///     search,
///     file_path:String::from("hello.txt"),
///     ignore_case:false,
/// };
///
/// ```
///
#[derive(Parser)]
#[command(name = "ifun-grep")]
#[command(author = "hboot <bobolity@163.com>")]
#[command(version = "0.2.0")]
#[command(about="A simple fake grep",long_about=None)]
pub struct Config {
    #[arg(short, long)]
    pub search: String,
    #[arg(short, long)]
    pub file_path: String,
    #[arg(short, long)]
    pub ignore_case: bool,
}

/// the fun is used to execute search
///
/// # example
/// ```
/// let search = String::from("let");
/// let config = ifun_grep::Config {
///     search,
///     file_path:String::from("hello.txt"),
///     ignore_case:false,
/// };
///
/// let result = ifun_grep::run(config);
///
/// assert!(result.is_ok());
/// ```
///
pub fn run(config: Config) -> Result<(), anyhow::Error> {
    let file_path = config.file_path.clone();
    let content = fs::read_to_string(config.file_path)
        .with_context(|| format!("could not read file {}", file_path))?;

    let result;
    if config.ignore_case {
        result = find_insensitive(&config.search, &content);
    } else {
        result = find(&config.search, &content);
    }
    for line in result {
        log::info!("{}", Red.on(Yellow).blink().paint(line));
    }

    Ok(())
}

/// the fun is used to execute search. it's case sensitive
///
/// # example
///
/// ```
/// let search = "rust";
/// let content = "\
/// nice. rust
/// I'm hboot.
/// hello world.
/// Rust
/// ";

/// assert_eq!(vec!["nice. rust"], ifun_grep::find(search, content));
/// ```
///
pub fn find<'a>(search: &str, content: &'a str) -> Vec<&'a str> {
    let mut result = vec![];
    for line in content.lines() {
        if line.contains(search) {
            // 符合，包含了指定字符串
            result.push(line);
        }
    }

    result
}
/// the fun is used to execute search. it's case sensitive
///
/// # example
///
/// ```
/// let search = "rust";
/// let content = "\
/// nice. rust
/// I'm hboot.
/// hello world.
/// Rust
/// ";
/// assert_eq!(vec!["nice. rust","Rust"], ifun_grep::find_insensitive(search, content));
/// ```
///
pub fn find_insensitive<'a>(search: &str, content: &'a str) -> Vec<&'a str> {
    let mut result = vec![];
    // 搜索 字符串转小写
    let search = search.to_lowercase();

    for line in content.lines() {
        // 文本行内容转小写
        if line.to_lowercase().contains(&search) {
            // 符合，包含了指定字符串
            result.push(line);
        }
    }

    result
}
